#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' main entry '
__author__ = 'pingwu you'

import time, threading
# 新线程执行的代码:
def loop():
    print('thread %s is running...' % threading.current_thread().name)
    n = 0
    while n < 5:
        n = n + 1
        print('thread %s >>> %s' % (threading.current_thread().name, n))
        time.sleep(1)
    print('thread %s ended.' % threading.current_thread().name)

def start_thread():
    print('thread %s is running...' % threading.current_thread().name)
    t = threading.Thread(target=loop, name='LoopThread')
    t.start()
    t.join()
    print('thread %s ended.' % threading.current_thread().name)

# 假定这是你的银行存款:
balance = 0
def change_it(n):
    # 先存后取，结果应该为0:
    global balance
    balance = balance + n
    balance = balance - n
def run_thread(n):
    for i in range(2000000):
        change_it(n)
def without_lock(): 
    global balance
    balance=0       
    t1 = threading.Thread(target=run_thread, args=(5,))
    t2 = threading.Thread(target=run_thread, args=(8,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(balance)

#每次获取到不同的锁，然后可以用同一个锁对某个资源进行保护
lock = threading.Lock()
lock2222 = threading.Lock()
print("第一个线程锁的id---"+str(id(lock)))
print("第二个线程锁的id---"+str(id(lock2222)))
def run_thread_lock(n):
    for i in range(2000000):
        # 先要获取锁:
        lock.acquire()
        try:            
            change_it(n)# 放心地改吧:
        finally:
            # 改完了一定要释放锁:
            lock.release()
def with_lock():   
    global balance
    balance=0      
    t1 = threading.Thread(target=run_thread_lock, args=(5,))
    t2 = threading.Thread(target=run_thread_lock, args=(8,))
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(balance)

"""
因为Python的线程虽然是真正的线程，但解释器执行代码时，有一个GIL锁：Global Interpreter Lock，
任何Python线程执行前，必须先获得GIL锁，然后，每执行100条字节码，解释器就自动释放GIL锁，让别的线程有机会执行。
这个GIL全局锁实际上把所有线程的执行代码都给上了锁，所以，多线程在Python中只能交替执行，
即使100个线程跑在100核CPU上，也只能用到1个核。

用单线程就好了，因为Python多线程本身就没什么效率，而且单线程也不用考虑同步问题了。
非要追求效率的话，就用多进程吧，同样也要考虑进程锁。
"""

# 创建全局ThreadLocal对象:
# 其实内部就是以线程id/对象的hashcode为key，存储在一个全局dict中，互不影响干扰的存取，不用加锁
# ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接，HTTP请求，用户身份信息等，这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
local_school = threading.local()
def process_thread(name):
    class Student():
        def __init__(self,name) -> None:
            self.name=name
        def __str__(self) -> str:
            return "student name is "+self.name
    # 绑定ThreadLocal的student:
    local_school.student = Student(name)
    std=local_school.student
    print('Hello, %s (in %s)' % (std, threading.current_thread().name))

def thread_local():  
    t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
    t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
    t1.start()
    t2.start()
    t1.join()
    t2.join()